package com.nulldev.util.JVM.reflections.cache;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

final class ConstructorObject {
	private ConstructorElement[] list;

	public ConstructorObject(Class<?> clazz) {
		Constructor<?>[] cs = clazz.getDeclaredConstructors();
		if (cs != null) {
			Constructor<?> c;
			int len = cs.length;
			List<ConstructorElement> lst = new ArrayList<ConstructorElement>(len);
			for (int i = 0; i < len; i++) {
				c = cs[i];
				if (Modifier.isPublic(c.getModifiers())) {
					lst.add(new ConstructorElement(c));
				}
			}
			Object[] o = lst.toArray();
			len = lst.size();
			list = new ConstructorElement[len];
			System.arraycopy(o, 0, list, 0, len);
		}
	}

	public Object newInstance(ClassLoader cl, Class<?>[] types, Object[] args) {
		ConstructorElement emt;
		int len = list.length;
		int argsLen = (args == null) ? 0 : args.length;
		try {
			if (argsLen == 0) {
				for (int i = 0; i < len; i++) {
					if ((emt = list[i]).paramsLength == 0) {
						return emt.constructor.newInstance(CachedPrimitive.NO_PARAM);
					}
				}
			} else {
				int pf = 100 * argsLen;
				int score = -1;
				ConstructorElement targetEmt = null;
				for (int i = 0; i < len; i++) {
					if ((emt = list[i]).paramsLength == argsLen) {
						int sc = Util.parmasScore(emt.primitives, emt.params, types, args, cl);
						if (sc != -1 && score < sc) {
							if (sc == pf) {
								return emt.constructor.newInstance(args);
							}
							score = sc;
							targetEmt = emt;
						}
					}
				}
				if (targetEmt != null) {
					if ((args = Util.convertParams(args, targetEmt.params)) == null) {
						throw new CachedReflectException("Parameter analysis failed.");
					}
					return targetEmt.constructor.newInstance(args);
				}
			}
			return null;
		} catch (InvocationTargetException it) {
			throw new CachedReflectException(it.getCause());
		} catch (Exception e) {
			throw new CachedReflectException(e);
		}
	}
}
